package cn.fscode.code.generator.service;

import cn.fscode.code.generator.model.po.IBasePO;
import cn.fscode.commons.core.utils.spring.SpringUtils;
import cn.hutool.core.util.StrUtil;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.reflections.Reflections;
import org.reflections.scanners.Scanners;
import org.reflections.util.ClasspathHelper;
import org.reflections.util.ConfigurationBuilder;
import org.reflections.util.FilterBuilder;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Set;

/**
 * 迁移数据服务
 * @author shenguangyang
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class MigrationDataService {
    @Value("${dataMigration.h2ToMysql:false}")
    private Boolean isH2ToMysql;

    @DS("mysql")
    public <T extends IBasePO> List<T> listMysqlData(Class<T> poClass) {
        String poClassName = poClass.getSimpleName().replace("PO", "");
        BaseMapper<T> baseMapper = SpringUtils.getBean(StrUtil.lowerFirst(poClassName) + "Mapper");
        return baseMapper.selectList(new QueryWrapper<>());
    }

    @DS("mysql")
    @Transactional
    public void saveDataToMysql(Class<? extends IBasePO> poClass,
                                List<? extends IBasePO> dataList) {
        log.info("save data to mysql, poClass: {}, dataSize: {}", poClass.getSimpleName(), dataList.size());
        String poClassName = poClass.getSimpleName().replace("PO", "");
        BaseMapper<Object> baseMapper = SpringUtils.getBean(StrUtil.lowerFirst(poClassName) + "Mapper");
        for (Object t : dataList) {
            baseMapper.insert(t);
        }
    }

    @DS("h2")
    public <T extends IBasePO> List<T> listH2Data(Class<T> poClass) {
        String poClassName = poClass.getSimpleName().replace("PO", "");
        BaseMapper<T> baseMapper = SpringUtils.getBean(StrUtil.lowerFirst(poClassName) + "Mapper");
        return baseMapper.selectList(new QueryWrapper<>());
    }

    @DS("h2")
    @Transactional
    public void saveDataToH2(Class<? extends IBasePO> poClass,
                                List<? extends IBasePO> dataList) {
        String poClassName = poClass.getSimpleName().replace("PO", "");
        BaseMapper<Object> baseMapper = SpringUtils.getBean(StrUtil.lowerFirst(poClassName) + "Mapper");
        for (Object t : dataList) {
            baseMapper.insert(t);
        }
    }

    /**
     * 开始迁移数据
     */
    public void migration() {
        if (!isH2ToMysql) {
            return;
        }
        log.info("start migration data...");
        MigrationDataService aopProxy = (MigrationDataService) AopContext.currentProxy();
        Reflections reflection = new Reflections(new ConfigurationBuilder()
                .setScanners(Scanners.SubTypes)
                .setUrls(ClasspathHelper.forPackage("cn.fscode.code.generator.model.po"))
                .filterInputsBy(new FilterBuilder().includePackage("cn.fscode.code.generator.model.po")));
        Set<Class<? extends IBasePO>> subTypesOf = reflection.getSubTypesOf(IBasePO.class);
        for (Class<? extends IBasePO> poClass : subTypesOf) {
            List<? extends IBasePO> dataList = aopProxy.listH2Data(poClass);
            aopProxy.saveDataToMysql(poClass, dataList);
        }
        log.info("end migration data");
        System.out.println("\n===> Please change H2_TO_MYSQL=false and USE_DB=mysql in startup.sh, and then restart it\n");
        System.exit(0);
    }
}
